#include <QScrollBar>
#include <QVBoxLayout>
#include <QPushButton>
#include <QPainter>
#include <QPainterPath>
#include <QTimer>
#include <QFileDialog>
#include <QMenu>

#include "debug.h"
#include "model/data.h"
#include "userinfowidget.h"
#include "mainwidget.h"
#include "model/datacenter.h"
#include "toast.h"
#include "messageshowarea.h"
#include "soundrecorder.h"

using namespace model;

MessageShowArea::MessageShowArea(QWidget* parent)
{
    //初始化基本属性
    this->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
    //让窗口大小跟着变化，必须设置这个选项
    this->setWidgetResizable(true);
    //设置滚动条的样式
    this->verticalScrollBar()->setStyleSheet("QScrollBar:vertical { width: 2px; background-color: rgb(240, 240, 240); }");
    this->horizontalScrollBar()->setStyleSheet("QScrollBar:horizontal { height: 0;}");
    this->setStyleSheet("QScrollArea { border: none; }");

    //创建Container的widget 作为包含容器的元素
    container = new QWidget();
    this->setWidget(container);

    //给container 添加布局管理器
    QVBoxLayout* layout = new QVBoxLayout();
    layout->setSpacing(0);
    layout->setContentsMargins(0,0,0,0);
    container->setLayout(layout);

#if TEST_UI
    for(int i=0; i<30; i++) {
        UserInfo userInfo;
        userInfo.userId = QString::number(1000);
        userInfo.nickname = "不摇碧莲";
        userInfo.description = "从今天开始认真敲代码";
        userInfo.avatar = QIcon(":/resource/image/avatarDefault.png");
        userInfo.phone = "15310432984";
       Message message = Message::messageFactory(TEXT_TYPE, "", userInfo, (QString("这是一条测试消息这是一条测试消息这是一条测试消息这是一条测试消息这是一条测试消息这是一条测试消息这是一条测试消息这是一条测试消息这是一条测试消息这是一条测试消息这是一条测试消息这是一条测试消息这是一条测试消息这是一条测试消息这是一条测试消息这是一条测试消息这是一条测试消息这是一条测试消息") + QString::number(i)).toUtf8(), "");
        this->addMessage(false,message);
    }

    // for (int i = 1; i <= 30; ++i) {
    //     UserInfo userInfo;
    //     userInfo.userId = QString::number(1000 + i);
    //     userInfo.nickname = "不摇碧莲" + QString::number(i);
    //     userInfo.description = "从今天开始认真敲代码";
    //     userInfo.avatar = QIcon(":/resource/image/avatarDefault.png");
    //     userInfo.phone = "18612345678";
    //     Message message = Message::messageFactory(TEXT_TYPE, "", userInfo, (QString("这是一条测试消息这是一条测试消息这是一条测试消息") + QString::number(i)).toUtf8(), "");
    //     this->addMessage(true, message);
    // }

#endif
}



//新增一个消息(尾插)
void MessageShowArea::addMessage(bool isLeft,const Message& message)
{
    //构造MessageItem 添加到布局管理器中
    MessageItem* messageItem = MessageItem::messageItemFactory(isLeft, message);
    container->layout()->addWidget(messageItem);
}

//新增一个消息(头插)
void MessageShowArea::addFrontMessage(bool isLeft,const Message& message)
{
    MessageItem* messageItem = MessageItem::messageItemFactory(isLeft, message);
    //container->layout() 是获取布局管理器，然后类型转换
    QVBoxLayout* layout = dynamic_cast<QVBoxLayout*>(container->layout());
    layout->insertWidget(0, messageItem);
}

//清空所有的消息
void MessageShowArea::clear()
{
    QLayout* layout = container->layout();
    for(int i = layout->count()-1; i>=0; i--) {
        QLayoutItem* item = layout->takeAt(i);
        if (item != nullptr && item->widget() != nullptr) {
            delete item->widget();
        }
    }
}

//设置滚动条滚动到末尾
void MessageShowArea::scrollToEndLater()
{
    // 拿到滚动条 获取到当前的最大值，然后设置滚动位置
    QTimer* timer = new QTimer();
    connect(timer,&QTimer::timeout,this,[=](){
        int maxValue = this->verticalScrollBar()->maximum();
        this->verticalScrollBar()->setValue(maxValue);

        timer->stop();
        timer->deleteLater();
    });

    timer->start(500);
}

/*
 *  表示一个消息
 *
 */

MessageItem::MessageItem(bool isLeft) :isLeft(isLeft)
{

}

//工厂模式创建MessageItem
MessageItem* MessageItem::messageItemFactory(bool isLeft,const Message& message)
{
    //创建对象和布局管理器
    MessageItem* messageItem = new MessageItem(isLeft);
    QGridLayout* layout = new QGridLayout();
    layout->setContentsMargins(30,10,40,0);
    layout->setSpacing(10);
    //messageItem 最低位100
    messageItem->setMinimumHeight(100);
    messageItem->setLayout(layout);

    //创建头像
    QPushButton* avatarBtn = new QPushButton();
    avatarBtn->setFixedSize(40,40);
    avatarBtn->setIconSize(QSize(40,40));
    avatarBtn->setIcon(message.sender.avatar);
    avatarBtn->setStyleSheet("QPushButton { border: none;}");
    if(isLeft) {
        //头像在0 行0列的位置， 占据2行1列
        layout->addWidget(avatarBtn,0,0,2,1,Qt::AlignLeft | Qt::AlignTop);
    }else {
        layout->addWidget(avatarBtn,0,1,2,1, Qt::AlignTop | Qt::AlignLeft);
    }

    //创建名字和时间
    QLabel* nameLabel = new QLabel();
    //设置发送者的名字和时间
    nameLabel->setText(message.sender.nickname + "|" + message.time);
    nameLabel->setAlignment(Qt::AlignBottom);
    nameLabel->setStyleSheet("QLabel { font-size: 12px; color: rgb(178, 178, 178); }");
    if(isLeft) {
        //放在0行1列的位置
        layout->addWidget(nameLabel,0,1);
    }else {
        layout->addWidget(nameLabel,0,0,Qt::AlignRight);
    }

    //创建消息体
    QWidget* contentWidget = nullptr;
    switch(message.messageType) {
    case TEXT_TYPE:
        contentWidget = createTextMessageItem(isLeft, message.content);
        break;
    case IMAGE_TYPE:
        contentWidget = createImageMessageItem(isLeft, message.fileId, message.content);
        break;
    case FILE_TYPE:
        contentWidget = createFileMessageItem(isLeft, message);
        break;
    case SPEECH_TYPE:
        contentWidget = createSpeechMessageItem(isLeft, message);
        break;
    default:
        LOG() << "错误的消息类型! messageType=" << message.messageType;
    }

    if(isLeft) {
        layout->addWidget(contentWidget,1,1);
    }else {
        layout->addWidget(contentWidget,1,0);
    }

    //添加信号槽
    //在聊天框中，点击用户头像，会显示出该用户的信息
    connect(avatarBtn,&QPushButton::clicked,messageItem,[=](){
        MainWidget* mainWidget = MainWidget::getInstance();
        UserInfoWidget* userInfoWidget = new UserInfoWidget(message.sender,mainWidget);
        userInfoWidget->exec();
    });

    if(!isLeft) {
        DataCenter* dataCenter = DataCenter::getInstance();
        connect(dataCenter,&DataCenter::changeNicknameDone,messageItem,[=](){
            nameLabel->setText(dataCenter->getMyself()->nickname);
        });

        connect(dataCenter,&DataCenter::changeAvatarDone,messageItem,[=](){
            UserInfo* userInfo = dataCenter->getMyself();
            LOG() << "修改头像";
            avatarBtn->setIcon(userInfo->avatar);
        });
    }

    return messageItem;
}

//创建对应的消息类型
QWidget* MessageItem::createTextMessageItem(bool isLeft,const QString& text)
{
    MessageContentLabel* messageContentLabel = new MessageContentLabel(text,isLeft,MessageType::TEXT_TYPE,"",QByteArray());
    return messageContentLabel;
}

QWidget* MessageItem::createImageMessageItem(bool isLeft,const QString& fileId,const QByteArray& content)
{
    MessageImageLabel* messageImageLabel = new MessageImageLabel(fileId,content,isLeft);
    return messageImageLabel;
}

QWidget* MessageItem::createFileMessageItem(bool isLeft,const Message& message)
{
    MessageContentLabel* messageContentLabel = new MessageContentLabel("[文件] " + message.fileName, isLeft, message.messageType,
                                                                      message.fileId, message.content);
    return messageContentLabel;
}

QWidget* MessageItem::createSpeechMessageItem(bool isLeft,const Message& message)
{
    MessageContentLabel* messageContentLabel = new MessageContentLabel("[语音]", isLeft, message.messageType,
                                                                       message.fileId, message.content);
    return messageContentLabel;
}


/*
 * 表示文本/文件消息的正文
 *
 */

MessageContentLabel::MessageContentLabel(const QString &text, bool isLeft, model::MessageType messageType, const QString& fileId,
                                         const QByteArray& content)
    : isLeft(isLeft), messageType(messageType), fileId(fileId), content(content)
{
    //设置SizePolicy
    this->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);

    QFont font;
    //设置字体
    font.setFamily("微软雅黑");
    font.setPixelSize(16);

    this->label = new QLabel(this);
    this->label->setAlignment(Qt::AlignCenter | Qt::AlignLeft);
    this->label->setText(text);
    this->label->setFont(font);
    this->label->setWordWrap(true); // 自动换行
    //控件内容和边框的空间 上下为0 左右为10 文本行高1.2  背景透明
    this->label->setStyleSheet("QLabel { padding: 0 10px; line-height: 1.2; background-color: transparent; }");

    // 对文件消息进行特殊判断
    if(messageType == MessageType::TEXT_TYPE) {
        return;
    }

    // 如果是文件消息，并且为空，向服务器获取内容
    if(this->content.isEmpty()) {
        DataCenter* dataCenter = DataCenter::getInstance();
        connect(dataCenter, &DataCenter::getSingleFileDone, this, &MessageContentLabel::updateUI);
        dataCenter->getSingleFileAsync(this->fileId);
    } else {
        this->loadContentDone = true;
    }
}

//这个函数会在控件被显示的时候 自动调用,即被添加到界面的时候
void MessageContentLabel::paintEvent(QPaintEvent* event)
{
    (void)event;
    // 获取到父亲的元素
    QObject* object = this->parent();
    if (!object->isWidgetType()) {
        //当前这个对象的父元素不是预期的QWidget 不需要操作
        return;
    }

    QWidget* parent = dynamic_cast<QWidget*>(object);
    int width =  parent->width() * 0.6; // 当消息达到父窗口的60% 就触发换行

    // 计算当前文本，如果是一行放置，需要多宽
    // 测量文本的尺寸和字体的相关信息。
    // label->font() 返回该标签使用的字体
    QFontMetrics metrics(this->label->font());
    int totalwidth = metrics.horizontalAdvance(this->label->text());

    // 计算出行数是多少 （40 代表左右各有20px的边距）
    int rows = (totalwidth /  (width-40)) +1;
    if(rows == 1) {
        width = totalwidth + 40;
    }

    // 根据行数，计算出高度，（20代表上下各有10px的边距）
    int height = rows * (this->label->font().pixelSize() * 1.2 ) + 20;

    //绘制圆角矩形和箭头
    QPainter painter(this);
    QPainterPath  path;
    //设置抗锯齿
    painter.setRenderHint(QPainter::Antialiasing);
    if(isLeft) {
        //使用QPainter的时候，画笔控制的是形状的边缘或轮廓，而画刷控制的是形状内部的填充颜色。
        painter.setPen(QPen(QColor(255, 255, 255)));
        painter.setBrush(QColor(255, 255, 255));

        //绘制圆角矩形
        painter.drawRoundedRect(10,0,width,height,10,10);
        //绘制箭头
        path.moveTo(10,15);
        path.lineTo(0,20);
        path.lineTo(10,25);
        path.closeSubpath(); //绘制的线性形成闭合的多边形，才能进行使用Brush填充颜色  会自动将起点和终点的相连
        painter.drawPath(path); // 绘制操作

        //这行代码将 label 的左上角位置设置为距离父控件左边缘 10 像素，距离顶部 0 像素，并且将其宽度和高度设置为 width 和 height。
        this->label->setGeometry(10,0,width,height);
    }else {
        painter.setPen(QPen(QColor(137, 217, 97)));
        painter.setBrush(QColor(137, 217, 97));

        //圆角矩形左侧边的横坐标位置
        int leftPos = this->width() - width - 10; // 10 用来表示箭头的宽度
        //圆角矩形右侧的的横坐标位置
        int rightPos = this->width() - 10;
        //绘制圆角矩形
        painter.drawRoundedRect(leftPos,0,width,height,10,10);
        //绘制箭头
        path.moveTo(rightPos, 15);
        path.lineTo(rightPos + 10, 20);
        path.lineTo(rightPos, 25);
        path.closeSubpath();
        painter.drawPath(path);

        this->label->setGeometry(leftPos,0,width,height);
    }

    //重新设置父元素的高度，因为消息长度不确定，可能有多行，还要包括名字和label的高度，以及多留一点空间
    parent->setFixedHeight(height+50);
}

//处理鼠标按下的操作
void MessageContentLabel::mousePressEvent(QMouseEvent *event)
{
    // 如果是左键，就另存为
    if(event->button() == Qt::LeftButton) {
        if(this->messageType == model::FILE_TYPE) {
            if(!this->loadContentDone) {
                Toast::showMessage("数据尚未加载成功, 请稍后重试");
                return;
            }
            saveAsFile(this->content);
        }else if(this->messageType == model::SPEECH_TYPE){
            if (!this->loadContentDone) {
                Toast::showMessage("数据尚未加载成功, 请稍后重试");
                return;
            }

            SoundRecorder* soundRecorder = SoundRecorder::getInstance();
            this->label->setText("播放中...");
            connect(soundRecorder, &SoundRecorder::soundPlayDone, this, &MessageContentLabel::playDone, Qt::UniqueConnection);
            soundRecorder->startPlay(this->content);
        }
    }
}

void MessageContentLabel::updateUI(const QString &fileId, const QByteArray &fileContent)
{
    if(this->fileId != fileId) {
        return;
    }

    this->content = fileContent;
    this->loadContentDone = true;
    this->update();
}

// 另存为文件
void MessageContentLabel::saveAsFile(const QByteArray &content)
{
    // 弹出对话框, 让用户选择路径
    QString filePath = QFileDialog::getSaveFileName(this,"另存为",QDir::homePath(),"*");
    if(filePath.isEmpty()) {
        LOG() << "用户取消了文件另存为";
        return;
    }

    writeByteArrayToFile(filePath,content);
}

void MessageContentLabel::playDone()
{
    if (this->label->text() == "播放中...") {
        this->label->setText("[语音]");
    }
}

void MessageContentLabel::contextMenuEvent(QContextMenuEvent *event)
{
    (void)event;
    if(messageType != SPEECH_TYPE) {
        LOG() << "非语音消息暂时不支持右键菜单";
        return;
    }

    QMenu* menu = new QMenu();
    menu->setStyleSheet(R"(
        QMenu {
            background-color: #2c3e50;
            border: 1px solid #34495e;
            border-radius: 5px;
            color: white;
        }
        QMenu::item {
            padding: 8px 20px;
            font-size: 14px;
        }
        QMenu::item:selected {
            background-color: #2980b9;
            color: white;
        }
        QMenu::item:pressed {
            background-color: #1abc9c;
        }
    )");
    QAction* action = menu->addAction("语音转文字");
    connect(action,&QAction::triggered,this,[=](){
        DataCenter* dataCenter = DataCenter::getInstance();
        connect(dataCenter,&DataCenter::speechConvertTextDone,this, &MessageContentLabel::speechConvertTextDone, Qt::UniqueConnection);
        dataCenter->speechConvertTextAsync(this->fileId,this->content);
    });

    // 弹出对话框
    menu->exec(event->globalPos());
    delete menu;
}

void MessageContentLabel::speechConvertTextDone(const QString &fileId, const QString &text)
{
    if(this->fileId != fileId) {
        return;
    }

    this->label->setText(text);
    this->update();
}

MessageImageLabel::MessageImageLabel(const QString &fileId, const QByteArray &content, bool isLeft)
    :fileId(fileId),content(content),isLeft(isLeft)
{
    //设置SizePolicy
    this->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);

    imageBtn = new QPushButton(this);
    imageBtn->setStyleSheet("QPushButton { border: none; }");
    if (content.isEmpty()) {
        // 此处这个控件, 是针对 "从服务器拿到图片消息" 这种情况.
        // 拿着 fileId, 去服务器获取图片内容
        DataCenter* dataCenter = DataCenter::getInstance();
        connect(dataCenter, &DataCenter::getSingleFileDone, this, &MessageImageLabel::updateUI);
        dataCenter->getSingleFileAsync(fileId);
    }
}

void MessageImageLabel::updateUI(const QString &fileId, const QByteArray &content)
{
    if(this->fileId != fileId) {
        return;
    }

    // 图片的内容
    this->content = content;

    // 触发paintEvent
    this->update();
}

void MessageImageLabel::paintEvent(QPaintEvent *event)
{
    (void)event;
    QObject* object = this->parent();
    if (!object->isWidgetType()) {
        //当前这个对象的父元素不是预期的QWidget 不需要操作
        return;
    }

    QWidget* parent = dynamic_cast<QWidget*>(object);
    int width = parent->width() * 0.6;

    // 加载图片
    QImage image;
    if (content.isEmpty()) {
        // 此时图片的响应数据还没回来.
        // 此处先拿一个 "固定默认图片" 顶替一下.
        QByteArray tmpContent = loadFileToQByteArray(":/resource/image/image.png");
        image.loadFromData(tmpContent);
    } else {
        // 此处的 load 操作 QImage 能够自动识别当前图片是啥类型的 (png, jpg....)
        image.loadFromData(content);
    }

    int height = 0;
    if(image.width() > width) {
        // 说明图片太宽了，进行等比例缩放
        height = ((double)image.height() / image.width()) * width;
    } else {
        width = image.width();
        height = image.height();
    }

    QPixmap pixmap = QPixmap::fromImage(image); // QImage 转换为 QPixmap
    imageBtn->setIconSize(QSize(width,height));
    imageBtn->setIcon(QIcon(pixmap));

    parent->setFixedHeight(height+50);

    if (isLeft) {
        imageBtn->setGeometry(10, 0, width, height);
    } else {
        int leftPos = this->width() - width - 10;
        imageBtn->setGeometry(leftPos, 0, width, height);
    }
}
